package com.buddy.learn.javasource._lambda;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.Data;
import org.junit.Test;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Buddy
 * @date 2021/5/13
 */
public class _StreamMap {

    @Test
    public void testMap() {
        List<String> list = students.stream()
                .map(StudentDTO::getCode)
                .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(list));
    }

    @Test
    public void testMapToInt() {
        String s0 = new String("abc");
        String s1 = new String("abc");
        System.out.println(s1.equals(s0));
        System.out.println(s1.intern().equals(s0.intern()));
        System.out.println(s1 == s0);
        System.out.println(s1.intern() == s0.intern());


        List<Integer> ids = students.stream()
                .mapToInt(s -> Integer.valueOf(s.getId() + ""))
                // 一定要有 boxed，因为 mapToInt 返回的是 IntStream，因为已经确定是 int 类型了
                // 所有没有泛型的，而 Collectors.toList() 强制要求有泛型的流
                // 所以需要使用 boxed 或者 mapToObj
                // 方法返回有泛型的流
                .boxed()
                .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(ids));

        // 计算学生总分
        Double sumScope = students.stream()
                .mapToDouble(StudentDTO::getScope)
                // DoubleStream/IntStream 有许多 sum（求和）、min（求最小值）、
                // max（求最大值）、average（求平均值）等方法
                .sum();
        System.out.println(sumScope);
    }

    @Test
    public void testFlatMap() {
        // 计算学生所有的学习课程，flatMap 返回 List<课程> 格式
        List<Course> courseList = students.stream().
                flatMap(s -> s.getLearningCources().stream()).
                collect(Collectors.toList());
        // 计算学生所有的学习课程，map 返回两层课程嵌套格式
        List<List<Course>> courseList2 = students.stream()
                .map(StudentDTO::getLearningCources)
                .collect(Collectors.toList());
        List<Stream<Course>> courseList3 = students.stream()
                .map(s -> s.getLearningCources().stream())
                .collect(Collectors.toList());

        System.out.println(JSON.toJSONString(courseList));
        System.out.println(JSON.toJSONString(courseList2));
        System.out.println(JSON.toJSONString(courseList3));
    }

    @Test
    public void testDistinct() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "b", "c", "A"));
        List<String> newList = list.stream().distinct().collect(Collectors.toList());
        System.out.println(JSON.toJSONString(newList));
    }

    @Test
    public void testSorted() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "d", "b", "b", "c", "A"));
        List<String> newList = list.stream()
                // 等同于 .sorted(Comparator.naturalOrder()) 自然排序
                .sorted()
                .collect(Collectors.toList());
        List<String> newReverseOrder = list.stream()
                // 反自然排序
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(newList));
        System.out.println(JSON.toJSONString(newReverseOrder));
    }

    @Test
    public void testLimit() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "d", "b", "b", "c", "A"));
        List<String> newReverseOrder = list.stream()
                // 反自然排序
                .sorted(Comparator.reverseOrder())
                .limit(2L)
                .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(newReverseOrder));
    }

    @Test
    public void testReduce() {
        List<Integer> list = new ArrayList<>(Arrays.asList(100, 300, 200, 110, 100));

        Integer s1 = list.stream()
                // scope1 和 scope2 表示循环中的前后两个数
                .reduce((i1, i2) -> i1 + i2)
                .orElse(0);
        System.out.println(s1);
        Integer s2 = list.stream()
                // 第一个参数表示成绩的基数，会从 100 开始加
                .reduce(100, (i1, i2) -> i1 + i2);
        System.out.println(s2);
    }

    @Test
    public void testFindFirst() {
        Long id = students.stream()
                .filter(s -> s.getName().equalsIgnoreCase("小美"))
                // 同学中有两个叫小美的，这里匹配到第一个就返回
                .findFirst()
                .get()
                .getId();
        System.out.println(id);
        // 防止空指针
        Long id2 = students.stream()
                .filter(s -> s.getName().equalsIgnoreCase("小li"))
                .findFirst()
                // orElse 表示如果 findFirst 返回 null 的话，就返回 orElse 里的内容
                .orElse(new StudentDTO()).getId();
        System.out.println(id2);


        Optional<StudentDTO> student= students.stream()
                .filter(s->s.getName().equalsIgnoreCase("小li"))
                .findFirst();
        // isPresent 为 true 的话，表示 value != null，即 student.get() != null
        if(student.isPresent()){
            System.out.println(student.get().getId());
        }
    }


    private final List<StudentDTO> students = new ArrayList<StudentDTO>() {
        {
            // 添加学生数据
            add(new StudentDTO(1L, "W199", "小美", "WM", 100D, new ArrayList<Course>() {
                {
                    // 添加学生学习的课程
                    add(new Course(300L, "语文"));
                    add(new Course(301L, "数学"));
                    add(new Course(302L, "英语"));
                }
            }));
            add(new StudentDTO(2L, "W25", "小美", "WM", 100D, Lists.newArrayList()));
            add(new StudentDTO(3L, "W3", "小名", "M", 90D, new ArrayList<Course>() {
                {
                    add(new Course(300L, "语文"));
                    add(new Course(304L, "体育"));
                }
            }));
            add(new StudentDTO(4L, "W1", "小蓝", "M", 10D, new ArrayList<Course>() {
                {
                    add(new Course(301L, "数学"));
                    add(new Course(305L, "美术"));
                }
            }));
        }
    };

    @Data
            // 学生数据结构
    class StudentDTO implements Serializable {

        private static final long serialVersionUID = -7716352032236707189L;

        public StudentDTO() {
        }

        public StudentDTO(Long id, String code, String name, String sex, Double scope,
                          List<Course> learningCources) {
            this.id = id;
            this.code = code;
            this.name = name;
            this.sex = sex;
            this.scope = scope;
            this.learningCources = learningCources;
        }

        /**
         * id
         */
        private Long id;
        /**
         * 学号 唯一标识
         */
        private String code;
        /**
         * 学生名字
         */
        private String name;

        /**
         * 性别
         */
        private String sex;

        /**
         * 分数
         */
        private Double scope;

        /**
         * 要学习的课程
         */
        private List<Course> learningCources;
    }

    @Data
            // 课程数据结构
    class Course implements Serializable {

        private static final long serialVersionUID = 2896201730223729591L;

        /**
         * 课程 ID
         */
        private Long id;

        /**
         * 课程 name
         */
        private String name;

        public Course(Long id, String name) {
            this.id = id;
            this.name = name;
        }
    }
}
